package cn.chenxing.module.file.service;

import cn.chenxing.constant.VenusConstant;
import cn.chenxing.domain.dto.TrackBox;
import cn.chenxing.domain.dto.TrackLine;
import cn.chenxing.domain.dto.TrackPoint;
import cn.chenxing.domain.dto.TrackTable;
import cn.chenxing.domain.threadlocal.ThreadData;
import cn.chenxing.domain.threadlocal.ThreadLocalInfo;
import cn.chenxing.domain.util.*;
import cn.chenxing.module.file.preprocess.DataPreProcessing;
import cn.chenxing.module.file.preprocess.KalManFilter;
import cn.chenxing.module.trajectory.entity.VenusTrackDataEntity;
import cn.chenxing.module.trajectory.entity.VenusTrackInfoEntity;
import cn.chenxing.module.trajectory.service.VenusTrackDataService;
import cn.chenxing.module.trajectory.service.VenusTrackInfoService;
import cn.chenxing.utils.SftpUtil;
import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import de.felixroske.jfxsupport.AbstractFxmlView;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.stage.Stage;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Description 文件Service层
 * @Author maogen.ymg
 * @Date 2020/3/21 20:23
 */
@Service
@Slf4j
public class FileService {

    @Resource
    private VenusTrackInfoService trackInfoService;

    @Resource
    private VenusTrackDataService trackDataService;

    /**
     * 前一个经度值(初始为0)
     */
    private double preLongitude = 0.0;
    /**
     * 前一个纬度值(初始为0)
     */
    private double preLatitude = 0.0;
    /**
     * 前一个时间戳(初始为0)
     */
    private double preTimeStamp = 0.0;

    /**
     * view方法打开新窗口 -- 文件结构分析窗口
     * @param view AbstractFxmlView
     */
    public void newStageByView(AbstractFxmlView view) {
        Stage stage = new Stage();
        JFXBaseUtil.setFileStructScreenBounds(stage);
        Parent root = view.getView();
        Scene scene = JFXBaseUtil.getDecoratorScene(stage, VenusConstant.FILE_STRUCT_TITLE, root);
        stage.setScene(scene);
        stage.show();
    }

    /**
     * 轨迹抽稀 -- Douglas-Peucker压缩
     */
    public void dpCompression() {
        DataPreProcessing.dpCompression();
    }

    /**
     * 卡尔曼滤波
     */
    public void kalManFilter() {
        new KalManFilter().kalManFilter();
    }

    /**
     * 轨迹过滤-- 中值滤波
     * @param compressCoordinate 压缩后的数据
     * @param minute 数据采样间隔时间（整数/分钟）
     * @return 滤波的数据
     */
    public TrackBox medianFiltering(List<TrackPoint> compressCoordinate, int minute) {
        return DataPreProcessing.medianFiltering1(compressCoordinate, minute);
    }

    /**
     * 获取数据库中用户的所有文件名列表
     * @return fileNameList
     */
    public List<String> getUserFileNameList() {
        List<String> fileNameList = new ArrayList<>();
        List<VenusTrackInfoEntity> trackInfoEntities = trackInfoService.list(
                new QueryWrapper<VenusTrackInfoEntity>().eq("user_name", ThreadLocalInfo.get().getUserName()));
        for(VenusTrackInfoEntity trackInfoEntity : trackInfoEntities) {
            fileNameList.add(trackInfoEntity.getFilePath());
        }
        return fileNameList;
    }

    /**
     * 将表格内的数据上传至服务器，并将文件详细信息添加到数据库
     * @param control 任一控件
     */
    public void uploadServer(Control control) {
        if(ThreadData.get().getTrackTableList() == null) {
            DialogBuilderUtil.showPositiveAlert(control, "无法上传数据", "表格内没有内容，请添加并预处理后重试");
            return;
        }
        // 远程目录
        String remoteDirectory = SftpUtil.getRemoteDirectory().concat("userData/");

        // 弹框获取文件名
        final boolean[] isCancel = {false};
        AtomicReference<String> stringAtomicReference = new AtomicReference<>();
        new DialogBuilderUtil(control)
                .setTitle("上传文件名")
                .setMessage("请给这个数据文件做个标记吧！")
                .setTextFieldText(result -> stringAtomicReference.set(result.trim()))
                .setPositiveBtn("确定")
                .setNegativeBtn("取消", () -> isCancel[0] = true).create();

        if(isCancel[0]) {
            return;
        }
        String fileName = stringAtomicReference.get();
        if(StringUtils.isBlank(fileName)) {
            fileName = ThreadLocalInfo.get().getUserName() + UUID.fastUUID().toString() + ".txt";
        }else {
            fileName = fileName.concat(".txt");
        }
        // 上传到服务器
        boolean isUpload = SftpUtil.upload(remoteDirectory, fileName, generateDataText().getBytes());
        if(isUpload) {
            // 上传成功
            // 轨迹文件信息
            VenusTrackInfoEntity trackInfoEntity = new VenusTrackInfoEntity();
            trackInfoEntity.setUserName(ThreadLocalInfo.get().getUserName())
                    .setFileName(fileName)
                    .setFilePath(remoteDirectory + fileName);
            trackInfoService.save(trackInfoEntity);
            // 查询文件ID
            trackInfoEntity = trackInfoService.getOne(
                    new QueryWrapper<VenusTrackInfoEntity>().eq("file_name", trackInfoEntity.getFileName()));

            // 轨迹数据信息
            for(TrackLine trackLine:ThreadData.get().getTrackData().getTrackLineList()) {
                VenusTrackDataEntity venusTrackDataEntity = new VenusTrackDataEntity();
                venusTrackDataEntity.setFileId(trackInfoEntity.getId())
                        .setDataId(trackLine.getId())
                        .setDistance(trackLine.getDistance())
                        .setLongitudeMin(trackLine.getLongitudeMin())
                        .setLongitudeMax(trackLine.getLongitudeMax())
                        .setLatitudeMin(trackLine.getLatitudeMin())
                        .setLatitudeMax(trackLine.getLatitudeMax())
                        .setAverageError(trackLine.getAverageError())
                        .setCompressRate(trackLine.getCompressRate());
                trackDataService.save(venusTrackDataEntity);
            }

            DialogBuilderUtil.showPositiveAlert(control, "上传文件",
                    "上传文件至服务器成功！保存地址为：" + remoteDirectory + fileName);
        }else {
            // 失败
            DialogBuilderUtil.showPositiveAlert(control, "上传文件",
                    "上传文件至服务器失败！");
        }

    }

    /**
     * 展示数据文件详细信息
     * @param control 任一控件
     */
    public void showDataInfo(Control control) {
        if(ThreadData.get().getTrackTableList() == null) {
            DialogBuilderUtil.showPositiveAlert(control, "无法显示信息", "表格内没有内容，请添加并预处理后重试");
            return;
        }
        List<TrackLine> trackLineList = ThreadData.get().getTrackData().getTrackLineList();
        StringBuilder sb = new StringBuilder();
        int i = 0;
        for(TrackLine trackLine:trackLineList) {
            sb.append("ID：").append(trackLine.getId()).append("\r\n");
            sb.append("距离：").append(trackLine.getDistance()/1000).append("公里\r\n");
            sb.append("经度：").append(trackLine.getLongitudeMin()).append("~").append(trackLine.getLongitudeMax()).append("\r\n");
            sb.append("纬度：").append(trackLine.getLatitudeMin()).append("~").append(trackLine.getLatitudeMax()).append("\r\n");
            sb.append("压缩率：").append(trackLine.getCompressRate()).append("%\r\n");
            sb.append("平均误差：").append(trackLine.getAverageError()).append("\r\n");
            if(i >= 4) {
                sb.append("……").append("\r\n");
                break;
            }
            i++;
        }
        sb.append("文件地址：");

        new DialogBuilderUtil(control).setPositiveBtn("确定")
                .setTitle("数据文件详细信息").setMessage(sb.toString()).setHyperLink(ThreadData.get().getFileUrl()).create();
    }

    /**
     * 导出表格数据到文件
     * @param control 任一控件
     */
    public void exportTable(Control control) {
        File saveTableFile = FileChooserUtil.saveTableFile("保存为",
                ThreadLocalInfo.get().getUserName() + UUID.fastUUID().toString() + ".txt");
        if(ThreadData.get().getTrackTableList() == null) {
            DialogBuilderUtil.showPositiveAlert(control, "无法导出数据", "表格内没有内容，请添加并预处理后进行导出");
            return;
        }

        if(saveTableFile == null) {
            return;
        }

        try (FileWriter out = new FileWriter(saveTableFile)) {

            out.write(generateDataText());
            log.info("表格文件已导出：{}", saveTableFile.getAbsolutePath());

        }catch (Exception e) {
            log.error("文件导出失败，原因为：{}", e.getLocalizedMessage(), e);
            DialogBuilderUtil.showPositiveAlert(control, "导出数据失败", "请重试导出数据！多次仍未解决请查看日志文件确定原因");
            return;
        }
        new DialogBuilderUtil(control).setPositiveBtn("确定")
                .setTitle("导出文件成功").setMessage("表格文件已导出：")
                .setHyperLink(saveTableFile.getAbsolutePath()).create();
    }

    /**
     * 判断前后是否是同一点 -- 将相同点过滤
     * @param longitude 经度
     * @param latitude 纬度
     * @return true or false
     */
    public boolean isSamePoint(double longitude, double latitude, long timeStamp) {
        // 1*10的-6次方即0.000001
        double w = 1e-6;
        boolean isSame = Math.abs(longitude - preLongitude) < w && Math.abs(latitude - preLatitude) < w
                && timeStamp == preTimeStamp;
        preLongitude = longitude;
        preLatitude = latitude;
        preTimeStamp = timeStamp;
        return isSame;
    }

    /**
     * 生成数据文本
     * @return String
     */
    private String generateDataText() {
        StringBuilder sb = new StringBuilder();
        if(ThreadData.get().isHasSpeed()) {
            sb.append("ID，Time,Longitude,Latitude,Speed\r\n");
        }else {
            sb.append("ID,Time,Longitude,Latitude\r\n");
        }
        for(TrackTable trackTable:ThreadData.get().getTrackTableList()) {
            if(ThreadData.get().isHasSpeed()) {
                sb.append(trackTable.getId()).append(",").append(trackTable.getTime()).append(",").append(trackTable.getLongitude())
                        .append(",").append(trackTable.getLatitude()).append(",").append(trackTable.getSpeed()).append("\r\n");
            }else {
                sb.append(trackTable.getId()).append(",").append(trackTable.getTime()).append(",")
                        .append(trackTable.getLongitude()).append(",").append(trackTable.getLatitude()).append("\r\n");
            }
        }
        return sb.toString();
    }

}
